#include "snmpinc.h"
#if SNMP_WANTED==1
#ifdef SNMP_STATISTIC
#define SNMPv1v2_INC_COUNTER
#endif
_INT16 Count_Var_Binds(LCL_FILE *stream, _UINT16 length)
{
	/* tell_place is the offset in stream to VarBind data */
	_UINT16 used, tell_place;
	_INT16 items;
	_INT16 asn1err = 0;
	_UINT16 alength=0, start_place=0, end_place=0;
	tell_place = Lcl_Tell(stream);
	for(items = 0, used = 0; used < length;)
	{
		start_place = Lcl_Tell(stream);
		if(Lcl_Eof(stream))
		{
			break;
		}
		/* Skip over the VarBind sequence */
		(void) A_DecodeTypeValue(stream, &asn1err);
		alength = A_DecodeLength(stream, &asn1err);
		if(asn1err || (Lcl_Seek(stream, alength, 1) < 0))
		{
			items = (DECODE_ASN_PARSE_ERROR);
			break;
		}
		end_place = Lcl_Tell(stream);
		used = used + end_place - start_place;
		items++;
		if(items>MAX_SUPPORT_OID_NUM)
		{
			return (MAX_SUPPORT_OID_NUM+1);
		}
	}
	(void) Lcl_Seek(stream, tell_place, 0);
	return items;
}
/****************************************************************************
NAME:  Decode_Pkt_To_Vb

PURPOSE:  Decode a VarBind from a packet
          On entry the input stream should be positioned to the tag field
          of the VarBind entry.
          On exit, the stream poINT32er will be positioned to at the start
          of the ASN.1 type field of AFTER the VarBind.

PARAMETERS:
        SNMP_PKT_T *    The packet being decoded.
        _UINT8   *    The input packet.

RETURNS:  success:  TRUE
          	  else FALSE
****************************************************************************/
_INT16 Decode_Pkt_To_Vb(LCL_FILE *stream, VB_T *vbp, SNMP_PKT_T *rp)
{
	_UINT8 flags;
	_UINT16 id;
	_UINT16 leng;
	_INT16 asn1err = 0;
	(void)A_DecodeObjectIdWTC(stream, &(vbp->vb_obj_id), &asn1err, A_OBJECTID, A_UNIVERSAL | A_PRIMITIVE);
	flags = A_DecodeTypeClass(stream);
	id = A_DecodeTypeValue(stream, &asn1err);
	leng = A_DecodeLength(stream, &asn1err);
	if(asn1err)
	{
		return (DECODE_ASN_PARSE_ERROR);
	}
	vbp->vb_data_length = leng;
	vbp->vb_data_flags_n_type = flags | (_UINT8) id;
	switch(vbp->vb_data_flags_n_type)
	{
	case VT_NUMBER:
		vbp->value_u.v_number = A_DecodeIntegerData(stream, leng, &asn1err);
		break;
	case VT_COUNTER:
	case VT_GAUGE:
	case VT_TIMETICKS:
		vbp->value_u.v_counter = (_UINT32) A_DecodeIntegerData(stream, leng, &asn1err);
		break;
	case VT_STRING:
	case VT_OPAQUE:
		(void)A_DecodeOctetStringData(stream, leng, vbp->value_u.v_string,MAX_OCTET_LENGTH,&asn1err);
		break;
	case VT_OBJECT:
		(void)A_DecodeObjectIdData(stream, leng, &(vbp->value_u.v_object), &asn1err);
		break;
	case VT_NOSUCHOBJ:
	case VT_NOSUCHINS:
	case VT_ENDOFMIB:
	case VT_EMPTY:
		/* Empty has no contents to be decoded */
		break;
	case VT_IPADDRESS:
		(void)A_DecodeOctetStringData(stream, leng,vbp->value_u.v_network_address,4,&asn1err);
		break;
	case VT_COUNTER64:
		if(rp->snmp_version == SNMP_VERSION_1)
		{
			return (DECODE_ASN_PARSE_ERROR);
		}
		(void)A_DecodeInteger64Data(stream, leng, &(vbp->value_u.v_counter64), &asn1err);
		break;
	default:
		return (DECODE_ASN_PARSE_ERROR);
	}
	if(asn1err)
	{
		return (DECODE_ASN_PARSE_ERROR);
	}
	else
	{
		return DECODE_NO_ERROR;
	}
	/*NOTREACHED*/
}

_INT16 Decode_Pkt_To_VbList(LCL_FILE *stream, SNMP_PKT_T *rp)
{
	VB_T *vbp;
	_INT16 i=0;
	_INT16 asn1err = 0;
	/* Now deal with the VarBindList and sanity check the length field */
	(void)A_DecodeTypeValue(stream, &asn1err);
	rp->pdu.vbl_length = A_DecodeLength(stream, &asn1err);
	if(asn1err || (rp->pdu.vbl_length != Lcl_Size(stream)))
	{
		return (DECODE_ASN_PARSE_ERROR);
	}
	/* Count the number of VarBinds.*/
	rp->pdu.vb_count = Count_Var_Binds(stream, rp->pdu.vbl_length);
	if(rp->pdu.vb_count>MAX_SUPPORT_OID_NUM)
	{
		//return (DECODE_ASN_PARSE_ERROR);
		rp->pdu.vb_count=MAX_SUPPORT_OID_NUM;
	}
	if(rp->pdu.vb_count >= 0xffff)
	{
		//return (DECODE_ASN_PARSE_ERROR);
		rp->pdu.vb_count=MAX_SUPPORT_OID_NUM;
	}
	/* The VarBindList has contents */
	for(i=0; i<rp->pdu.vb_count; i++)
	{
		vbp=(VB_T *)(&(rp->pdu.vb_obj[i]));
		(void)A_DecodeTypeValue(stream, &asn1err);
		vbp->vb_seq_size = A_DecodeLength(stream, &asn1err);
		if(asn1err || Decode_Pkt_To_Vb(stream, vbp, rp))
		{
			return (DECODE_ASN_PARSE_ERROR);
		}
	}
	return DECODE_NO_ERROR;
}

_INT16 SNMP_Decode_PDU(SNMP_PKT_T *rp, LCL_FILE *in_stream)
{
	_INT16 asn1err = 0;
	_INT16 non_rep = 0;
	_INT16 max_rep = 1;
	_INT16 Remain;
	/* if we get here it's a non-trap form of pdu */
	rp->pdu.non_rep=0;
	rp->pdu.max_rep=1;
	SNMP_ERR_STATUS(rp)=0;
	SNMP_ERR_INDEX(rp)=0;
	rp->pdu.request_id = (_INT32)A_DecodeIntegerWTC(in_stream, &asn1err,A_INTEGER, A_UNIVERSAL | A_PRIMITIVE);
	non_rep = (_UINT16)A_DecodeIntegerWTC(in_stream, &asn1err,A_INTEGER, A_UNIVERSAL | A_PRIMITIVE);
	max_rep = (_UINT16)A_DecodeIntegerWTC(in_stream, &asn1err,A_INTEGER, A_UNIVERSAL | A_PRIMITIVE);
	if(asn1err)
	{
		return (DECODE_ASN_PARSE_ERROR);
	}
	/* Now deal with the VarBindList */
	asn1err=Decode_Pkt_To_VbList(in_stream,rp);
	if(asn1err)
	{
		return (DECODE_ASN_PARSE_ERROR);
	}
#if GET_BULK_SUPPORT==1
	if(rp->pdu_type==GET_BULK_REQUEST_PDU)
	{
		if(rp->snmp_version!=SNMP_VERSION_2)
		{
			return (DECODE_ASN_PARSE_ERROR);
		}
		if(max_rep < 1)
		{
			non_rep=rp->pdu.vb_count;
			max_rep=1;
		}
		else
		{
			if(non_rep<0)
			{
				non_rep=0;
			}
			else if(non_rep>=rp->pdu.vb_count)
			{
				non_rep=rp->pdu.vb_count;
				max_rep=1;
			}
			else
			{
				Remain=rp->pdu.vb_count-non_rep;
				if((non_rep+Remain*max_rep)>=MAX_SUPPORT_OID_NUM)
				{
					max_rep=(MAX_SUPPORT_OID_NUM-non_rep)/Remain;
				}
			}
		}
		rp->pdu_type=GET_NEXT_REQUEST_PDU;
	}
	else
	{
		non_rep=rp->pdu.vb_count;
		max_rep=1;
	}
#else
	if(rp->pdu_type==GET_BULK_REQUEST_PDU)
	{
		if(rp->snmp_version!=SNMP_VERSION_2)
		{
			return (DECODE_ASN_PARSE_ERROR);
		}
		rp->pdu_type=GET_NEXT_REQUEST_PDU;
	}
	non_rep=rp->pdu.vb_count;
	max_rep=1;
#endif
	rp->pdu.non_rep=non_rep;
	rp->pdu.max_rep=max_rep;
	return DECODE_NO_ERROR;
}

/****************************************************************************
NAME:  SNMP_Decode_V1_Packet

PURPOSE:  Decode an V1 (RFC1157) or V2 SNMP packet.

PARAMETERS:
        SNMP_PKT_T *    Packet we're filling in.
        _UINT8 *    original received packet.
        _UINT32	  packet length
        _UINT8 *    error return, if it is (_INT32 *)0, don't
                        return an error.

RETURNS:  void
****************************************************************************/

_INT16 SNMP_Decode_V1V2_Packet(SNMP_PKT_T *rp,LCL_FILE *in_stream,_UINT16 asn1leng)
{
	_UINT16 ptype;
	_UINT16 plength;
	_INT16 asn1err = NO_ERROR;
	_UINT16 error_status=0;
	/* Extract the community string */
	memset(rp->community,0,sizeof(rp->community));
	(void)A_DecodeOctetStringWTC(in_stream, rp->community,MAX_COMMUNITY_LEN,
	                             &asn1err,A_OCTETSTRING, A_UNIVERSAL | A_PRIMITIVE);
	if(asn1err)
	{
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+1;//GEN_ERR;/*new add*/
		return (-1);
	}
	/* Decode the packet type                                             */
	/* Since all of the PDUs follow the same form, we can decode them     */
	/* without being concerned as to which PDU we are decoding.           */
	/* Furthermore, since the VarBindList is the last thing in the PDU,   */
	/* we can ignore the overall length of the sequence forming the PDU.  */
	if(A_DecodeTypeClass(in_stream) != (A_DEFAULT_SCOPE | A_CONSTRUCTOR))
	{
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+2;//GEN_ERR;/*new add*/
		return (-1);
	}
	/* Decode the pdu type */
	ptype = A_DecodeTypeValue(in_stream, &asn1err);
	plength = A_DecodeLength(in_stream, &asn1err);
	if(asn1err)
	{
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+3;//GEN_ERR;/*new add*/
		return (-1);
	}
	/* Determine if we support the given pdu for the given version */
	switch(rp->snmp_version)
	{
	case SNMP_VERSION_1:
	case SNMP_VERSION_2:
		break;
	default:
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInBadVersions);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+4;//GEN_ERR;/*new add*/
		return (-1);
	}
	/* Check whether there is an inconsistency between the PDU length and */
	/* the overall length indicated by the outermost ASN.1 wrapper.       */
	/* If so, the packet is ill-formed and must be rejected.              */
	if(asn1leng != (Lcl_Tell(in_stream) + plength))
	{
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+5;//GEN_ERR;/*new add*/
		return (-1);
	}
	rp->pdu_type = (_UINT8)ptype;
	rp->pdu_length = plength;
	/* Check that the community string is valid.  This also gets the      */
	/* view mask for this transaction from the community string.  The     */
	/* source and destination addresses are passed through for the user   */
	/* routine to deal with.  It may just copy them INT32o the snmp packet  */
	/* structure.
	*/
#if 1
	switch(SNMP_Community_Validate(rp))
	{
	case SNMP_COMMUNITY_ERR_NO_ERROR:
		break;
	case SNMP_COMMUNITY_ERR_BAD_COMM:
	default:
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInBadCommunityNames);
#endif
		error_status=AUTHORIZATION_ERROR;
		break;
	}
#endif
	switch(SNMP_Decode_PDU(rp, in_stream))
	{
	case 0:
	{
		SNMP_ERR_STATUS(rp)=error_status;
		if(SNMP_ERR_STATUS(rp)!=NO_ERROR)
		{
			return (-1);
		}
		else
		{
			return NO_ERROR;
		}
	}
	case DECODE_ASN_PARSE_ERROR:
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+6;//GEN_ERR;/*new add*/
		return (-1);
	case DECODE_ALLOCATION_FAILURE:
	default:
		SNMP_ERR_STATUS(rp)=USER_ERR+7;//GEN_ERR;/*new add*/
		return (-1);
	}
	/*return NO_ERROR;*/
}


_INT16 SNMP_Decode_Packet_WER(_UINT8 *ebuffp,_UINT16 pktl,SNMP_PKT_T *rp)
{
	_UINT16 type;
	_UINT16 overall_length, asn1leng;
	_UINT8 flags;
	LCL_FILE *in_stream, in_pkt_stream;
	_INT16 asn1err = 0;
	_INT16 snmp_version;
	/* if the caller did not supply error_ret set that up and
	 * init it to the no error state */
	SNMP_ERR_STATUS(rp) = NO_ERROR;
	/* Make sure the size is within range and then Set up packet as a local stream. */
	if((in_stream = Lcl_Open(&in_pkt_stream, ebuffp, (_UINT16) pktl)) == 0)
	{
		SNMP_ERR_STATUS(rp) = USER_ERR+8;//GEN_ERR;/*new add*/
		return (-1);
	}
	/* Decode the top-level message sequence... */
	flags = A_DecodeTypeClass(in_stream);
	type = A_DecodeTypeValue(in_stream, &asn1err);
	overall_length = A_DecodeLength(in_stream, &asn1err);
	if(asn1err)
	{
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+9;//GEN_ERR;/*new add*/
		return (-1);
	}
	/* Validate that the length provided by the caller is consistent with */
	/* the length given by the ASN.1 wrapper...                           */
	/* If necessary, shrink the local I/O buffer to match.                */
	asn1leng = Lcl_Tell(in_stream) + overall_length;
	if(asn1leng < (_UINT16) pktl)
	{
		in_stream->lbuf_end = (_UINT8 *)(in_stream->lbuf_start + asn1leng);
		if(in_stream->lbuf_next < in_stream->lbuf_end)
		{
			in_stream->lcl_flags &= ~LCL_EOF;
		}
		else
		{
			in_stream->lcl_flags |= LCL_EOF;
		}
	}
	else
	{
		if(asn1leng > (_UINT16) pktl)
		{
#ifdef SNMP_STATISTIC
			SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
			SNMP_ERR_STATUS(rp)=USER_ERR+10;//GEN_ERR;/*new add*/
			return (-1);
		}
	}
	/* Test the flags & type info to see if we have something that
	 * looks like the right kind of packet */
	if((flags | type) != (A_UNIVERSAL | A_CONSTRUCTOR | A_SEQUENCE))
	{
		/* oh well, this isn't an snmp packet, bail out */
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+11;//GEN_ERR;/*new add*/
		return (-1);
	}
	/* get the version stamp */
	snmp_version = (_INT16)A_DecodeIntegerWTC(in_stream, &asn1err, A_INTEGER, A_UNIVERSAL | A_PRIMITIVE);
	if(asn1err)
	{
#ifdef SNMP_STATISTIC
		SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
#endif
		SNMP_ERR_STATUS(rp)=USER_ERR+12;//GEN_ERR;/*new add*/
		return (-1);
	}
	rp->snmp_version = snmp_version;
	switch(snmp_version)
	{
	case SNMP_VERSION_1:
	case SNMP_VERSION_2:
		asn1err = SNMP_Decode_V1V2_Packet(rp,in_stream,asn1leng);/*new add error_ret*/
		break;
	default:
#ifdef SNMP_STATISTIC
		if((snmp_version < SNMP_VERSION_MIN) || (snmp_version > SNMP_VERSION_MAX))
		{
			SNMPv1v2_INC_COUNTER(snmp_stats.snmpInASNParseErrs);
		}
		else
		{
			SNMPv1v2_INC_COUNTER(snmp_stats.snmpInBadVersions);
		}
#endif
		asn1err = -1;
		SNMP_ERR_STATUS(rp)=USER_ERR+13;//GEN_ERR;/*new add*/
		break;
	}
	if(asn1err)
	{
		return (-1);
	}
	else
	{
		return (NO_ERROR);
	}
}
#endif
